package com.pajk.user.service.credit;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.pajk.user.api.CreditRuleService;
import com.pajk.user.dal.mapper.CreditRuleMapper;
import com.pajk.user.exception.CreditServiceException;
import com.pajk.user.service.common.tair.TairNamespaceEnum;
import com.taobao.tair.DataEntry;
import com.taobao.tair.Result;
import com.taobao.tair.ResultCode;
import com.taobao.tair.TairManager;
import com.pajk.user.model.CreditRule;
import org.springframework.transaction.annotation.Transactional;

public class CreditRuleServiceImpl implements CreditRuleService{

    private static final Logger log             = LoggerFactory.getLogger(CreditRuleServiceImpl.class);

    private static final String dataIdSeparator = ".";

    private static final int    tairNameSpace   = TairNamespaceEnum.CREDITRULE.getSpaceNum();

    private static final String DATA_ID_PREFIX = "com.pajk.user.credit";

    @Resource
    private TairManager         tairManager;
    @Resource
    private CreditRuleMapper    creditRuleMapper;
    
    public CreditRuleWrap getCredits(long userId, String actionCode, Date actionTime) {
        CreditRuleWrap creditRule = getCreditRule(userId, actionCode, actionTime);
        if (creditRule.getTimes() <= 0) {
            creditRule.setOverTimes(true);
            return creditRule;
        }
        registerTimesInTair(creditRule);
        return creditRule;
    }

    private void registerTimesInTair(CreditRuleWrap creditRule) {
        Result<DataEntry> result = tairManager.get(tairNameSpace, creditRule.getTairKey());
        if (result.getRc() == ResultCode.DATANOTEXSITS) {
            int version = 2;
            if (creditRule.getExpiredDate() != null) {
                ResultCode resultCode = tairManager.put(tairNameSpace, creditRule.getTairKey(), 1,
                    version, (int) (creditRule.getExpiredDate().getTime() / 1000));
                if (!resultCode.isSuccess()) {
                    throw new CreditServiceException("putToTair Error!resultCode=" + resultCode);
                }
            } else {
                putToTair(tairNameSpace, creditRule.getTairKey(), 1, version);
            }
        } else if (result.getRc() == ResultCode.SUCCESS) {
            int times = Integer.valueOf(result.getValue().getValue().toString());
            if (times >= creditRule.getTimes()) {
                creditRule.setOverTimes(true);
                return;
            }
            putToTair(tairNameSpace, creditRule.getTairKey(), times + 1, result.getValue()
                .getVersion());

        } else {
            log.error("fetch rule statsu from tair failed, error code: " + result.getRc().getCode()
                      + " error msg: " + result.getRc().getMessage());
            throw new CreditServiceException("fetch rule statsu from tair failed");
        }
    }

    public void rollbackTimesFromTair(CreditRuleWrap creditRule) {
        Result<DataEntry> result = tairManager.get(tairNameSpace, creditRule.getTairKey());
        if (result.getRc() != ResultCode.SUCCESS) {
            throw new CreditServiceException("can't get credit rule from tair!creditRule="
                                             + creditRule);
        }
        int times = Integer.valueOf(result.getValue().getValue().toString());
        tairManager.put(tairNameSpace, creditRule.getTairKey(), times - 1);
    }

    public CreditRuleWrap getCreditRule(long userId, String actionCode, Date actionTime) {
        CreditRule creditRule = getCreditRule(actionCode.toLowerCase());
        CreditRuleWrap creditRuleWrap = new CreditRuleWrap(creditRule);
        creditRuleWrap.init(userId, actionTime);
        return creditRuleWrap;
    }

    private void putToTair(int namespace, Serializable key, Serializable value, int version) {
        ResultCode resultCode = tairManager.put(namespace, key, value, version);
        if (!resultCode.isSuccess()) {
            throw new CreditServiceException("putToTair Error!resultCode=" + resultCode);
        }
    }

    private CreditRule getCreditRule(String actionCode) {
        CreditRule creditRule = getCreditRuleFromCache(DATA_ID_PREFIX + dataIdSeparator + actionCode);
        if (creditRule != null) {
            return creditRule;
        }
        creditRule = getCreditRuleByActionCode(actionCode);
        if(creditRule==null){
            log.error("can't find creditRule by actionCode=" + actionCode);
            throw new CreditServiceException("can't find creditRule by actionCode=" + actionCode);
        }
        updateCreditRuleCache(creditRule);
        return creditRule;
    }
    
    private CreditRule getCreditRuleFromCache(String actionCode) {
        Result<DataEntry> result = tairManager.get(tairNameSpace, actionCode);
        if (result.getRc() != ResultCode.SUCCESS) {
            return null;
        }
        return (CreditRule) result.getValue().getValue();
    }
    
    private void updateCreditRuleCache(CreditRule creditRule) {
        boolean updateSuccess = false;
        int expireTime = 24 * 60 * 60;
        String actionCode = creditRule.getActionCode();
        ResultCode resultCode = tairManager.put(tairNameSpace, actionCode, creditRule, expireTime);
        if (!resultCode.isSuccess()) {
            int tryTime = 3;
            while (tryTime > 0) {
                ResultCode reTryRc = tairManager.put(tairNameSpace, actionCode, creditRule,
                    expireTime);
                if (reTryRc.isSuccess()) {
                    updateSuccess = true;
                    break;
                } else {
                    tryTime--;
                }
            }
        } else {
            updateSuccess = true;
        }
        if (!updateSuccess) {
            deleteCreditRuleCache(creditRule.getActionCode());
        }
    }
    
    private void deleteCreditRuleCache(String actionCode) {
        ResultCode resultCode = tairManager.delete(tairNameSpace, actionCode);
        if (!resultCode.isSuccess()) {
            int tryTime = 3;
            while (tryTime > 0) {
                ResultCode reTryRc = tairManager.delete(tairNameSpace, actionCode);
                if (reTryRc.isSuccess()) {
                    break;
                } else {
                    tryTime--;
                }
            }
        }
    }
    
    @Override
    @Transactional(value="userTransactionManager")
    public void insertCreditRule(CreditRule creditRule) {
        creditRuleMapper.insertCreditRule(creditRule);
        deleteCreditRuleCache(creditRule.getActionCode());
    }

    @Override
    public CreditRule getCreditRuleByActionCode(String actionCode) {
        return creditRuleMapper.getCreditRuleByActionCode(actionCode);
    }

    @Override
    @Transactional(value="userTransactionManager")
    public int updateCreditRuleByActionCode(CreditRule creditRule) {
        int affectedRows = creditRuleMapper.updateCreditRuleByActionCode(creditRule);
        updateCreditRuleCache(creditRule);
        return affectedRows;
    }

    @Override
    @Transactional(value="userTransactionManager")
    public int deleteCreditRuleByActionCode(String actionCode) {
        int affectedRows = creditRuleMapper.deleteCreditRuleByActionCode(actionCode);
        deleteCreditRuleCache(actionCode);
        return affectedRows;
    }

    @Override
    public List<CreditRule> getCreditRule() {
        return creditRuleMapper.getCreditRule();
    }
}
